{
Traviesadora beta 0.2.0.13.33 (30.11.2008)
LastMod: 18.03.2009 by Prof1983

Copyright 2008, travieorama@googlemail.com

This file is part of the taviesadora project

taviesadora is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

taviesadora is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
}
unit NobraProtocoll;

interface

uses
  SysUtils, Forms, Classes, ExtCtrls, Windows, Dialogs, Controls,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdTCPServer, IdGlobal, IdHTTP,
  IdSocketHandle, IdSocks, IdIOHandlerSocket, IdException,
  ChatDistributer, Service, RosterDB, NobraClientThread, RosterGlobals,
  {VibDriver,} InetCommunication, DialogWin; {MovieLibrary;}

type
  PIdPeerThread = ^TIdPeerThread;

  TCLientThreadItem = record
    CLientThread: TNobraClientThread;
    closing: Boolean;
  end;
  PCLientThreadItem = ^TCLientThreadItem;

  TNobraNet = class(TInetCom)
  public
    constructor create(Owner: TObject; iniFile: TConfigFile; RosterDB: TRosterDB{; VibDriver: TVibHardwareDriver});
    destructor destroy;  override;
    procedure ReloadConfig;
    function StartChat(UserID:string;Nick:string):TChatDistri;
    procedure CloseChat(UserID:string);
    procedure SendMsg(UserID,Msg:string);
    function GetPresence:string;
    function IsEnabled:boolean;
    procedure Connect;
    procedure CancelConnect;
    procedure Disconnect;
    function IsConnected:boolean;
    function IsLoginError:boolean;
    procedure SetContactState(ContactState: TContactState);
    function GetLoginState: string;
    function GetNick: string;
  private
    IdHTTP: TIdHTTP;
    TCPServr:  TIdTCPServer;
    TCPClientToBeDestroyedList:TList;
    ClientThreadList:TList;
    Terminating:boolean;
    ReConnectTimer:TTimer;
    //vibDriver: TVibHardwareDriver;
    Owner:TObject;
    iniFile:TConfigFile;
    CanConnect:boolean;
    Retrys:integer;
    connectionError:boolean;
    connected:boolean;
    ThreadSave_StartChatReturn:Tobject;
    ThreadSave_UserID:string;
    ThreadSave_RmtNick:string;
    ThreadSave_StartChatReturnSemaphore:RTL_CRITICAL_SECTION;  //Protects  ThreadSave_StartChatReturn
    procedure OnReconnectTimer(Sender: TObject);
    procedure OnClientConnectsToMyServer(AThread: TIdPeerThread);
    procedure ReConnect;
    function StartChatAsClient(UserID:string;Nick:string):TChatDistri;
    procedure ProcessInputChatString(AThread: TIdPeerThread;msg:string);
    procedure OnThreadTermaninates(Sender: TObject);
    procedure ThreadSave_StartChatAsServer;
    procedure ThreadSave_SetRmtNick;
    procedure ThreadSave_GetAcceptEntry;
    procedure ThreadSave_GetAcceptAnswer;
    procedure OnIdTCPServerExecute(AThread: TIdPeerThread);
    procedure OnClientDisconnects(AThread: TIdPeerThread);
    procedure ThreadSave_SendMsg;
    procedure ShowClosedMsgBox;
  end;

function TranslateNobraVibToTravieVib(s: string): string;
function TranslateNobraToTravie(s: string): string;
function TranslateTravieToNobra(s: string; nick: string): string;
function TranslateTravieVibToNobraVib(s: string; nick: string): string;
function isTravieUserAtNobra(nick: string): boolean;
function DelNobraRscString(nick: string): string;
function UserIDToIPPort(UserID: string; var Port: Integer): string;

implementation

uses
  NobraChatAcceptWin, TraviesadoraCoreImpl;

{ Procedures }

  function isTravieUserAtNobra(nick: string): Boolean;
  var
    i:integer;
  begin
    i :=  pos('/traviesadora',nick);
    result := i > 0;
  end;

  function DelNobraRscString(nick: string): string;
  var
    i:integer;
  begin
    result := nick;
    i := pos('/traviesadora',result);
    if i > 0 then begin
      delete(result,i,length(result));
    end;
  end;

{"<$>a" (vibi steuerungsbefehl, a-o, p)}
  function TranslateNobraVibToTravieVib(s:string):string;
  var
    i:integer;
    c:integer;
  begin
    result := '';
    for i := length(s)-2 downto 1 do begin
      if s[i] = #$A then begin
        delete(s,1,i);
        break;
      end;
    end;
    s := trim(s);
    i := pos('<$>',s);
    if i>0 then begin
      if s[i+3] <> 'p' then
        c := ord(s[i+3])-ord('a')+1
      else
        c := 0;
      c := 100-round(100*c/15);
      result := '{none}0'+sep+inttostr(c);
    end;
  end;

  function TranslateNobraToTravie(s: string): string;
  begin
    result := TranslateNobraVibToTravieVib(s);
    if result <> '' then
      result := '{vibrx}{channelall}'+result
    else
      result := s;
  end;

  function TranslateTravieVibToNobraVib(s: string; Nick: string): string;
  var
    i: Integer;
    vibsign: Boolean;
  begin
    result := s;
    if not isTravieUserAtNobra(nick) then
    begin
      vibsign := true;
      if vibsign then
      begin
        i := pos('}',s);
        while (i > 0) do
        begin
          delete(s,1,pos('}',s));
          i := pos('}',s);
        end;
        if (pos('synch',result) > 0) or (pos('rxbar',result)>0) or (pos('vibtx',result)>0)or (pos('config',result)>0) then
          result := ''
        else
        begin
          if (Pos('<$>',s) = 0) then
          begin
            Result := '<$>';
            delete(s,1,pos(sep,s)+1);
            i := 100-StrToIntDef(s,100);
            i := round(15*i/100);
            if i = 0 then
              Result := result+'p'
            else
              Result := result+chr(ord('a')+i-1);
          end
          else
            Result := s;
        end;
      end;
    end;
  end;

  function TranslateTravieToNobra(s: string; Nick: string): string;
  var
    vibsign: Boolean;
  begin
    result := s;
    if not isTravieUserAtNobra(nick) then
    begin
      if (Pos('{vibtx}',s) > 0) then
        Result := ''
      else if s = '{vib_question_stopped}' then
        Result := ''
      else
      begin
        vibsign := pos('{vibrx}',s)>0;
        if vibsign then
          Result := TranslateTravieVibToNobraVib(s,nick);
      end;
    end;
  end;

  function UserIDToIPPort(UserID: string; var Port: Integer): string;
  begin
    result := UserID;
    Delete(result,pos(':',result),length(result));
    Delete(UserID,1,pos(':',UserID));
    Port := StrToInt(UserID);
  end;
{ TNobraNet }

constructor TNobraNet.create(Owner: TObject; iniFile: TConfigFile; RosterDB: TRosterDB{; vibDriver:TVibHardwareDriver});
begin
  inherited create(rosterdb);
  CanConnect := false;
  Terminating := false;
  self.Owner := Owner;
  InitializeCriticalSection(ThreadSave_StartChatReturnSemaphore);
 // InitializeCriticalSection(AcceptSemaphore);
  Retrys := 0;
  connectionError := false;
  connected := false;
  self.rosterDB := rosterdb;
  self.iniFile := iniFile;
  {
  self.vibDriver := vibDriver;
  }
  ClientThreadList := TList.Create;
  TCPClientToBeDestroyedList := TList.create;

  TCPServr := TIdTCPServer.Create(nil);
  with TCPServr do begin
      OnConnect := OnClientConnectsToMyServer;
      OnExecute := OnIdTCPServerExecute;
      OnDisconnect := OnClientDisconnects;
      Name := 'NobraServer';
     // OnClientRead  := OnServerRead;
     // OnClientDisconnect:= OnClientDisconnectsFromMyServer;
     // OnClientError := OnServerError;
  end;
  IdHTTP := TIdHTTP.create(nil);
  with IdHTTP do begin
    AllowCookies := False;
    ProxyParams.BasicAuthentication := False;
    ProxyParams.ProxyPort := 0;
    Request.ContentLength := -1;
    Request.ContentRangeEnd := 0;
    Request.ContentRangeStart := 0;
    Request.Accept := 'text/html, */*';
    Request.BasicAuthentication := False;
    Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
    HTTPOptions := [hoForceEncodeParams];
  end;
  ReConnectTimer := TTimer.create(nil);
  ReConnectTimer.OnTimer := OnReconnectTimer;
  ReloadConfig;
end;

destructor TNobraNet.destroy;
var
  i:integer;
  ticks:Cardinal;
  ClientThreadItem:PCLientThreadItem;
  list:TList;
begin
  Terminating := true;

  if TCPServr.Active then begin
    try
      CloseChat('all');
      ticks := GetTickCount;
      repeat
        Application.ProcessMessages;
        list := TCPServr.Threads.LockList;
        i := list.Count;
        TCPServr.Threads.UnlockList;
      until (i = 0) or (GetTickCount-ticks > 5000);
    except on EIdTerminateThreadTimeout do begin end;
    end;
  end;
   for i := 0 to ClientThreadList.Count-1 do begin
    ClientThreadItem := ClientThreadList[i];
    if ClientThreadItem.CLientThread is TNobraClientThread then
        if not ClientThreadItem.closing then begin
          (ClientThreadItem.CLientThread as TNobraClientThread).SetShowChatClosedWin(false);
          (ClientThreadItem.CLientThread as TNobraClientThread).Terminate;
      end;
  end;       
  TCPClientToBeDestroyedList.Destroy;
  try
    TCPServr.Destroy;
  except on EIdTerminateThreadTimeout do begin end;
  end;
  ReConnectTimer.destroy;
  IdHTTP.Destroy;
  inherited destroy;
end;

procedure TNobraNet.ReloadConfig;
var
  s:string;
  i:integer;
begin
  s := iniFile.GetNobraPseudo;
  delete(s,pos('/',s),length(s));
  s := s+'/traviesadora-'+GetMyAppVersionString;
  for i := 0 to length(s) do begin
    if s[i] = ' ' then
      s[i] := '_';
  end;
  iniFile.SetNobraPseudo(s);
  TCPServr.DefaultPort:= iniFile.GetNobraPort;
  TCPServr.Active := iniFile.GetNobraNetEnabled and CanConnect;
  if not (iniFile.GetNobraNetEnabled and CanConnect)then
    connected :=false;
  ReConnect;
  with ReConnectTimer do begin
    if connected and not connectionError then
      Interval := iniFile.GetNobraServerTime*900
    else
      interval := 3000;
    Enabled :=  iniFile.GetNobraNetEnabled and CanConnect;
  end;
end;

procedure TNobraNet.OnReconnectTimer(Sender: TObject);
begin
  ReloadConfig;
end;

{{connect (urlStr,80); //tcp,http-port

tmpStr = format("GET %s/nobrachat.pl?name=%s&port=%d&hw=%c&t=%d HTTP/1.0"
,subdir
,pseudo
,port
,hardwaretype
,refreshtime);

tmpStr += 13;
tmpStr += 10;
tmpStr += "Host: ";
tmpStr += urlStr;
tmpStr += 13;
tmpStr += 10;
tmpStr += 13;
tmpStr += 10;

send(tmpStr);}

procedure TNobraNet.ReConnect;
{var
  resp:TStringList;
  STokeNizer:TStringTokenizer;
  requ:string;
  s:string;
  ID:string;
  Nick:string;
  v:string;
  i:integer;
  exception:boolean;}
begin
  (*
  exception := false;
  if iniFile.GetNobraNetEnabled and CanConnect then begin
    resp := TStringList.Create;
    try
      try
        with IdHTTP do
        begin
          Host := iniFile.GetNobraServer;
          port := 80;
          Connect(500);
          s := uppercase(vibDriver.GetHardwareDesc);
          if s <> '' then
            s := s[1]
          else
            s := 'N';
          if s = 'V' then
            s := 'S';
          requ := iniFile.GetNobraServerSub+'/nobrachat.pl?name='+iniFile.GetNobraPseudo;
          requ := requ+'&port='+inttostr(iniFile.GetNobraPort);

          requ := requ+'&hw='+s;
          requ := requ+'&t='+IntToStr(iniFile.GetNobraServerTime);

          resp.Text := Get(requ);
          Disconnect;
        end;
        self.connected := true;
        RosterDB.SetOwnContactState(csAvailable,ntNobraNet);
{

// antwort ist selbsterklaerend

"HTTP/1.1 200 OK" = connected
"<#>Pseudo;IP;port;N;" = client 1, no hardware
"<#>Pseudo;IP;port;N;" = client 2, no hardware
"<#>Pseudo;IP;port;V;" = client 3, vibrator
etc ...

<#>JustMe;213.39.227.12;27100;no;

die anfrage wird nach "refreshtime" wiederholt

---------------------------------------------- }
        rosterDB.StartAddModeNobra;
        STokeNizer := TStringTokenizer.Create('');
        STokeNizer.SetTrenner(';');
        try
        for i := 0 to resp.Count-1 do begin
          s := resp[i];
          delete(s,1,pos('>',s));
          STokeNizer.SetText(s);
          Nick := STokeNizer.NextToken;
          ID := STokeNizer.NextToken+':';
          ID := ID+STokeNizer.NextToken;
          v := LowerCase(STokeNizer.NextToken);
          if length(v)>0 then
            v := v[1]
          else
            v := 'n';
          rosterDB.AddNobra(ID,nick,v='s',iniFile.GetNobraIsBlocked(nick),iniFile.GetNobraIsWhite(nick));
        end;
        finally
          STokeNizer.Free;
        end;
        rosterDB.EndAddModeNobra;
        Retrys := 0;
        connectionError := false;
      except

        on EIdConnectTimeout do begin
          exception := true;
        end;
        on EIdSocketError   do begin
           exception := true;
        end;
      end;
      if exception then begin
        if not Connected then
          RosterDB.SetOwnContactState(csOffline,ntNobraNet);
        Connected := false;
        inc(Retrys);
        if Retrys > 5 then begin
          rosterDB.StartAddModeNobra;
          rosterDB.EndAddModeNobra;
          connectionError := true;
        end;
      end;
    finally
      resp.Free;
    end;
  end;
  *)
end;

{

chat-protokoll nobrachat
------------------------

server tcp, port 27100 (egal, lauft ueberall)
jedes nobrachat ist server und client.
daten sind ascii-textbasiert.

client: macht tcp connection zum server (IP/port)
server: "<01>"
client: "<02:pseudo>"
server: "<03:ok>" (angenommen)
oder: "<03:ko>" (abgelehnt)

client oder server:
"textmeldung" (ascii)
"<$>a" (vibi steuerungsbefehl, a-o, p)
}

procedure TNobraNet.ThreadSave_StartChatAsServer;
begin
  ThreadSave_StartChatReturn := TConData.create(ThreadSave_UserID,'',iniFile.GetNobraPseudo,self,False);
end;

procedure TNobraNet.ThreadSave_SetRmtNick;
begin
  (ThreadSave_StartChatReturn as TConData).Nick := ThreadSave_RmtNick;
  (ThreadSave_StartChatReturn as TConData).ChatDistributor.SetRmtNick(ThreadSave_RmtNick);
end;

procedure TNobraNet.ThreadSave_GetAcceptEntry();
var
  ButtonList: TStringList;
  s: string;
begin
  with ThreadSave_StartChatReturn as TConData do
  begin
    Accepted := asNothing;
    if iniFile.GetNobraAcceptAll then
      Accepted := asAccepted
    else
    begin
      if iniFile.GetNobraIsBlocked(ThreadSave_RmtNick) then
        Accepted := asBlocked
      else if iniFile.GetNobraIsWhite(ThreadSave_RmtNick) then
        Accepted := asAccepted
      else
      begin
        SetChatAcceptAnswer(ncaNone);
        ButtonList:=TStringList.Create;
        try
          ButtonList.Add('Yes');
          ButtonList.Add('Always yes for this user');
          ButtonList.Add('No');
          ButtonList.Add('Block');
          s := ThreadSave_RmtNick;
          delete(s,pos('/',s),length(s));
          SetfrmChatAcceptAnswer(TFrmDialog.CreateNotModal(ThreadSave_StartChatReturn,
                                                          'Wanna start Chat?',
                                                          'Wanna start a chat with '+s+'?',
                                                          ButtonList
                                                          ));
        finally
          ButtonList.Free;
        end;
      end;
    end;
  end;
end;

procedure TNobraNet.ThreadSave_GetAcceptAnswer;
begin
  with ThreadSave_StartChatReturn as TConData do begin
    if GetChatAcceptAnswer=ncaYes then
      Accepted := asAccepted
    else if GetChatAcceptAnswer=ncaAlwaysYes then begin
      Accepted := asAccepted;
      iniFile.AddNobraWhite(Nick);
    end else if GetChatAcceptAnswer = ncaNo then
      Accepted := asBlocked
    else if GetChatAcceptAnswer = ncaBlock then begin
      Accepted := asBlocked;
      iniFile.AddNobraBlock(Nick);
    end;
    if (GetfrmChatAcceptAnswer = nil) and (Accepted = asNothing)  then
      Accepted := asBlocked;
    end;
end;
                      
procedure TNobraNet.ThreadSave_SendMsg;
begin
  (ThreadSave_StartChatReturn as TConData).ChatDistributor.NewIncomingChatMsg((ThreadSave_StartChatReturn as TConData).RX);
end;

procedure TNobraNet.ProcessInputChatString(AThread: TIdPeerThread;msg:string);
begin
  entercriticalsection(ThreadSave_StartChatReturnSemaphore);
  ThreadSave_StartChatReturn := AThread.Data;
  (ThreadSave_StartChatReturn as TConData).RX := msg;
  AThread.Synchronize(ThreadSave_SendMsg);
  LeaveCriticalSection(ThreadSave_StartChatReturnSemaphore);
end;

procedure TNobraNet.OnClientConnectsToMyServer(AThread: TIdPeerThread);
var
  s,t:string;
begin
  try
  entercriticalsection(ThreadSave_StartChatReturnSemaphore);
  ThreadSave_UserID := AThread.Connection.Socket.Binding.PeerIP+':'+Inttostr(AThread.Connection.Socket.Binding.PeerPort);///!!!!!!!  ThreadSave_UserID not protcted yet
  AThread.Synchronize(ThreadSave_StartChatAsServer);
  AThread.Data := ThreadSave_StartChatReturn;
  LeaveCriticalSection(ThreadSave_StartChatReturnSemaphore);

  (AThread.Data as TConData).SetShowChatClosedWin(false);
  AThread.Connection.WriteLn('<01>');
  s := AThread.Connection.ReadLn(LF,1000);
  t := s;
  if pos('<02:',lowercase(s))>0 then
  begin
    delete(t,1,pos(':',t));
    delete(t,pos('>',t),length(t));
    entercriticalsection(ThreadSave_StartChatReturnSemaphore);  //ENTER
    ThreadSave_StartChatReturn := AThread.Data;
    ThreadSave_RmtNick := t;
    AThread.Synchronize(ThreadSave_SetRmtNick);
    LeaveCriticalSection(ThreadSave_StartChatReturnSemaphore); //LEAVE


    (AThread.Data as TConData).Accepted := asNothing;
    entercriticalsection(ThreadSave_StartChatReturnSemaphore);  //ENTER
    ThreadSave_StartChatReturn := AThread.Data;
    AThread.Synchronize(ThreadSave_GetAcceptEntry);
    LeaveCriticalSection(ThreadSave_StartChatReturnSemaphore); //LEAVE

   // AThread.Priority := tpLower;
    if (AThread.Data as TConData).Accepted = asNothing then begin
      repeat
        entercriticalsection(ThreadSave_StartChatReturnSemaphore);  //ENTER
        ThreadSave_StartChatReturn := AThread.Data;
        AThread.Synchronize(ThreadSave_GetAcceptAnswer);
        LeaveCriticalSection(ThreadSave_StartChatReturnSemaphore); //LEAVE
        sleep(100);
        AThread.Connection.ReadLn(LF,1);//for producing an exception if the partner closed the connection
      until ((AThread.Data as TConData).Accepted <> asNothing) or AThread.Stopped;
    end;
    AThread.Priority := tpNormal;

    if  (AThread.Data as TConData).Accepted = asAccepted then begin
                  (AThread.Data as TConData).SetShowChatClosedWin(true);
      AThread.Connection.WriteLn('<03:OK>');
      ProcessInputChatString(AThread,'');
      AThread.Synchronize((AThread.Data as TConData).ChatDistributor.ProtocollIsReadyForTransferMsgs);
    end else begin
      (AThread.Data as TConData).SetShowChatClosedWin(false);
      AThread.Connection.WriteLn('<03:KO>');
      AThread.stop;
      //AThread.Connection.Disconnect;
    end;

  end;
  except on EIdException do begin
 //   AThread.stop;
    //  AThread.Connection.Disconnect;
    end;
  end;
end;

procedure TNobraNet.OnIdTCPServerExecute(AThread: TIdPeerThread);
var
  s: string;
  TX:TStringList;
begin
  try
    s := AThread.connection.ReadLn(LF,10);
    if not AThread.connection.ReadLnTimedOut then
    begin
      ProcessInputChatString(AThread,s);
    end;
    TX := (AThread.Data as TConData).TX;
    while (tx.count > 0) do
    begin
      AThread.connection.Writeln(tx[0]);
      tx.Delete(0);
    end;
  except
    on EIdClosedSocket do
    begin
    end;
  end;
end;

procedure TNobraNet.ShowClosedMsgBox;
begin
  MessageDlg('Chat closed',mtInformation,	[mbOK],0);
end;

procedure TNobraNet.OnClientDisconnects(AThread: TIdPeerThread);
var
  myuserID: string;
begin
  if (AThread.Data as TConData).GetShowChatClosedWin then
    AThread.Synchronize(ShowClosedMsgBox);
  myuserID := (AThread.Data as TConData).UserID;
  AThread.Synchronize((AThread.Data as TConData).CloseChatDistri);
end;

function TNobraNet.StartChat(UserID: string; Nick: string): TChatDistri;
begin
  Result := StartChatAsClient(UserID, Nick);
end;

function TNobraNet.StartChatAsClient(UserID, Nick: string): TChatDistri;
var
  ClientThreadItem: PCLientThreadItem;
begin
  new(ClientThreadItem);
  ClientThreadItem.closing := False;
  ClientThreadItem.CLientThread := TNobraClientThread.Create(False);
  ClientThreadItem.CLientThread.Init(UserID, Nick, IniFile.GetNobraPseudo, Self);
  ClientThreadItem.CLientThread.OnTerminate := OnThreadTermaninates;
  ClientThreadList.Add(ClientThreadItem);

  Result := ClientThreadItem.CLientThread.GetChatDistri;
end;

procedure TNobraNet.OnThreadTermaninates(Sender: TObject);
var
  ClientThreadItem: PCLientThreadItem;
  i: Integer;
begin
  for i := 0 to ClientThreadList.Count-1 do
  begin
    ClientThreadItem := ClientThreadList[i];
    if ClientThreadItem.CLientThread  = Sender then begin
      ClientThreadItem.closing := true;
      if ClientThreadItem.CLientThread is TNobraClientThread then
        (ClientThreadItem.CLientThread as TNobraClientThread).destroy;
      Dispose(ClientThreadItem);
      ClientThreadList.Delete(i);
    end;
  end;
end;

procedure TNobraNet.SendMsg(UserID, Msg: string);
var
  ClientThreadItem:PCLientThreadItem;
  i:integer;
  List: TList;
  found,all:boolean;
  //pThread: PIdPeerThread;
  ConData:TConData;
  //s:string;
begin
  found := false;
  all := UserID = 'all';
  for i := 0 to ClientThreadList.Count-1 do begin
    ClientThreadItem := ClientThreadList[i];
    if ClientThreadItem.CLientThread is TNobraClientThread then
      if ((ClientThreadItem.CLientThread as TNobraClientThread).getuserID = UserID) or all then begin
        (ClientThreadItem.CLientThread as TNobraClientThread).sendMsg(msg);
        found := true;
      end;
  end;
  if not found  or all then begin
    List := TCPServr.Threads.Locklist;
    try
      for i := 0 to List.Count - 1 do begin
        if Assigned(List[i]) then begin
          try
            ConData := TConData(TIdPeerThread(List[i]).data);
          except
            ConData := nil;
          end;
          if Assigned(ConData) then begin
            if (ConData.UserID = UserID) or (userID = 'all') then begin
              ConData.TX.Add(msg);
              if not all then
                break;
            end;
          end;
        end;
      end;
    finally
      TCPServr.Threads.unlockList;
    end;
  end;
end;

procedure TNobraNet.CloseChat(UserID: string);
var
  ClientThreadItem:PCLientThreadItem;
  i:integer;
  List: TList;
  found,all:boolean;
  //pThread: PIdPeerThread;
  //s:string;
  ConData:TConData;
begin
  found := false;
  all := UserID = 'all';
  for i := 0 to ClientThreadList.Count-1 do begin
    ClientThreadItem := ClientThreadList[i];
    if ClientThreadItem.CLientThread is TNobraClientThread then
      if ((ClientThreadItem.CLientThread as TNobraClientThread).getuserID = UserID) or all then begin
        if not ClientThreadItem.closing then begin
          (ClientThreadItem.CLientThread as TNobraClientThread).SetShowChatClosedWin(false);
          (ClientThreadItem.CLientThread as TNobraClientThread).Terminate;
        end;
        found := true;
      end;
  end;
  if not found or all then begin
    List := TCPServr.Threads.Locklist;
    try
      for i := 0 to List.Count - 1 do begin
        if Assigned(List[i]) then begin
          try
            ConData := TConData(TIdPeerThread(List[i]).data);
          except
            ConData := nil;
          end;
          if Assigned(ConData) then begin
            if (ConData.UserID = UserID) or (userID = 'all') then begin
              TIdPeerThread(List[i]).Stop;
              ConData.SetShowChatClosedWin(false);
              if not all then
                break;
            end;
          end;
        end;
      end;
    finally
      TCPServr.Threads.unlockList;
    end;
  end;
end;

procedure TNobraNet.Connect;
begin
  connectionError := False;
  Retrys := 0;
  CanConnect := True;
  ReloadConfig;
  TravCore.IniCryptLayer(ntNobraNet);
end;

procedure TNobraNet.CancelConnect;
begin
  CanConnect := false;
  ReloadConfig;
end;

procedure TNobraNet.Disconnect;
begin
  CanConnect := false;
  RosterDB.SetOwnContactState(csOffline,ntNobraNet);
  RosterDB.clearNobra;
  ReloadConfig;
  //form1.StopCryptLayer(ntNobraNet);
end;

function TNobraNet.IsConnected: boolean;
begin
  result := connected;
end;

function TNobraNet.IsEnabled: boolean;
begin
  result := CanConnect;
end;

function TNobraNet.GetPresence: string;
begin
  if IsConnected then
    result := 'Online'
  else
    Result := 'Offline';
end;

procedure TNobraNet.SetContactState(ContactState: TContactState);
begin
  if ContactState = csOffline then
  begin
    Disconnect
  end else
    Connect;
end;

function TNobraNet.GetLoginState(): string;
begin
  if CanConnect then begin
    if connected then
      result := 'Connected'
    else
      result := 'Connecting..'
  end else
    result := 'Offline';
  if connectionError then
    result := 'Connection Error';
end;

function TNobraNet.GetNick:string;
begin
  result := iniFile.GetNobraPseudo;
end;

function TNobraNet.IsLoginError: boolean;
begin
  result := connectionError;
end;








end.
